/* 
 * File:   Conexion.cpp
 * Author: max
 * 
 * Created on October 20, 2012, 9:03 AM
 */
#include <iostream>
#include "client_Conexion.h"
#include "stdlib.h"
#include "errno.h"
#include "common_ProtocoloConstantes.h"

using namespace TP::Comunicacion;

#define BUF_SIZE 500

Conexion::Conexion(const string& pIp, const string& pPuerto) : Thread (0) 
{
    this->ip = pIp;
    this->puerto = pPuerto;
    this->manejadorConexionEvento = NULL;
}

Conexion::~Conexion() {}

void Conexion::Iniciar() throw (Excepcion&){
    
    try
    {
        if ( -1 == (this->sockfd = socket(AF_INET, SOCK_STREAM, 0) ))
        {
            string msg = "No se puede iniciar el socket. ";
            msg += strerror(errno);
            throw Excepcion ( msg);
        }
        
        struct sockaddr_in dest_addr;
        
        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = htons(atoi(this->puerto.c_str()));
        dest_addr.sin_addr.s_addr = inet_addr(this->ip.c_str());
        memset(&(dest_addr.sin_zero), '\0', 8);

        if (connect (this->sockfd, (struct sockaddr*) &dest_addr, sizeof (struct sockaddr)) == -1)
        {
            throw Excepcion ("No se puede conectar al host " 
                             + this->ip 
                             + " puerto " 
                             + this->puerto 
                             + ". "
                             + strerror (errno) );
        }

        // Inicio la escucha de conexiones entrantes.
        Thread::Iniciar();
    }
    catch (Excepcion& pEx)
    {
        if (-1 != this->sockfd)
            shutdown (this->sockfd, SHUT_RDWR);
        
        pEx.expandirMensaje("No se puede iniciar la conexion. ");
        throw;
    }
    
}

void Conexion::Finalizar() throw (Excepcion&){
    
    try
    {
        this->Liberar();  // Indico que Conexion ya no trabaja mas.
        this->manejadorConexionEvento = NULL;
        
        // Se finaliza la conexion.
        close(this->sockfd);
        
        // Finalizo el ciclo de trabajo de la conexion.
        Thread::Finalizar();

    }
    catch (Excepcion& pEx)
    {
        pEx.expandirMensaje("No se puede finalizar la conexion.");
        throw;
    }
}

void Conexion::agregarManejadorConexionEvento(IManejadorConexionEvento* pManejador){
    if (pManejador)
        this->manejadorConexionEvento = pManejador;
}

int Conexion::Escribir(const string& pMensaje) throw (Excepcion&){
    
    int cntBytes = -1;
    
    if (this->ConexionPerdida)
        throw Excepcion ("Conexion perdida.");
    
    try
    {
        /*
         * Toma el mensaje, y lo escribe a traves de la conexion.
         */
        string msg_env = pMensaje;
        
        // Finalizo el mensaje para transmision.
        msg_env += FIN_MENSAJE;

        // Envio el mensaje.
        cntBytes = send (this->sockfd, msg_env.c_str(), msg_env.size(), 0);
        
        this->manejarErrorComunicacion(cntBytes);
    }
    catch (Excepcion& pEx)
    {
        throw;
    }
    
    return cntBytes;
}

void Conexion::Responder (const string& pMensaje) throw (Excepcion&){ /*dummy*/}

void Conexion::tarea(){ 

    /* Llegado el caso, aca es donde se tiene que encodear el mensaje para el envio.
     *      
     */
    string mensaje = "";
    char buffer [256];
    bzero(buffer,256);
    bool reciviendo = true;
    int bytes_recv = -1;
    
    while (reciviendo)
    {
        bytes_recv = recv(this->sockfd, buffer, 255, 0);
        
        if (bytes_recv <= 0)
            break; /* Se produjo un error en la recepcion.*/
        
        // Vuelco el buffer en el mensaje.
        for (int i = 0; i < bytes_recv; i++){
            
            if (FIN_MENSAJE != buffer[i])
                mensaje += buffer[i];
            else
                reciviendo = false;
        }
        
        // Reinicio el buffer en caso de tener que seguir reciviendo data.
        bzero(buffer,256);
    }
    
    if (bytes_recv > 0)
    {
        // El mensaje esta completo.  
        
        // Decodifico el mensaje 
        for (unsigned int i = 0; i < mensaje.size(); i++)
            if (SALTO_LINEA == mensaje[i])
                mensaje [i] = '\n';
        
        // Notifico a los manejadores de mensaje entrante.
        
        this->manejadorConexionEvento->notificarMensajeEntrante((IMensajero*) this, mensaje);        
        mensaje = "";
    }
    else 
        if (0 == bytes_recv)
            this->ConexionPerdida = true;
}

void Conexion::manejarErrorComunicacion (int pError) throw (Excepcion&) {
    
    string mensaje = "";

    if (pError == -1)
    {
        mensaje = "error\n";
        throw Excepcion (mensaje);
    }
    else if (pError == 0)
    {
        /*Se perdio la conexion con el servidor.  Informo al cliente...*/
        mensaje = "CONEXION_PERDIDA\n";
        throw Excepcion (mensaje);
    }
}

